#pragma once
#include <string>
#include <iostream>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/resource.h>
#include "../comm/util.hpp"
#include "../comm/log.hpp"

class Runner
{
public:
    // 编译增加时间和内存限制参数
    static void SetProcLimit(int cpu_limit, int mem_limit)
    {
        // 设置cpu限制
        struct rlimit cpu_rlimit;
        cpu_rlimit.rlim_max = RLIM_INFINITY;
        cpu_rlimit.rlim_cur = cpu_limit;
        setrlimit(RLIMIT_CPU, &cpu_rlimit);

        // 设置内存限制
        struct rlimit mem_rlimit;
        mem_rlimit.rlim_max = RLIM_INFINITY;
        mem_rlimit.rlim_cur = mem_limit*1024;   // 默认转为kb
        setrlimit(RLIMIT_AS, &mem_rlimit);
    }
    /******************************************
     * 指明文件名，不需要全路径
     * ----------------------------------------
     * 返回 > 0 程序异常，收到返回值信号
     * 返回 = 0 正常运行完毕，结果保存在临时文件
     * 返回 < 0 内部错误
     *******************************************/
    static int Run(const std::string& file_name, int cpu_limit, int mem_limit)
    {
        /****************************************
         * 程序执行情况：
         * 1.代码跑完，结果正确
         * 2.代码跑完，结果不正确
         * 3.代码没跑完，异常
         * 结果的正确与否不考虑，由oj题型判断
         * 只考虑是否正确运行
         * 
         * 标准输入：不处理，留接口备用
         * 标准输出：运行完毕，输出结果
         * 标准错误：运行错误信息
         ****************************************/
        // 列出文件
        std::string execute_file = PathUtil::PathExe(file_name);
        std::string stdin_file = PathUtil::Stdin(file_name);
        std::string stdout_file = PathUtil::Stdout(file_name);
        std::string stderr_file = PathUtil::Stderr(file_name);
        // 执行结果写入三个文件，打开
        umask(0);
        int stdin_fd = open(stdin_file.c_str(), O_CREAT | O_RDONLY, 0644);
        int stdout_fd = open(stdout_file.c_str(), O_CREAT | O_WRONLY, 0644);
        int stderr_fd = open(stderr_file.c_str(), O_CREAT | O_WRONLY, 0644);
        if (stdin_fd < 0 || stdout_fd < 0 || stderr_fd < 0)
        {
            LOG(ERROR) << "运行文件打开失败\n";
            return -1;  // 打开失败
        }

        // 创建子进程
        pid_t pid = fork();
        if (pid < 0)
        {
            LOG(ERROR) << "创建子进程失败\n";
            close(stdin_fd);
            close(stdout_fd);
            close(stderr_fd);
            return -2;  // 创建子进程失败
        }
        else if (pid == 0)
        {
            // 重定向
            dup2(stdin_fd, 0);
            dup2(stdout_fd, 1);
            dup2(stderr_fd, 2);
            // 设置限制
            SetProcLimit(cpu_limit, mem_limit);
            // 执行程序
            
            execl(execute_file.c_str(), execute_file.c_str(), nullptr);
            exit(0);
        }
        else
        {
            int status = 0;
            waitpid(pid, &status, 0);
            close(stdin_fd);
            close(stdout_fd);
            close(stderr_fd);
            // 运行异常会收到信号
            LOG(INFO) << "运行结果:" << (status & 0x7f) << "\n";
            return status & 0x7f;
        }
    }
};